#include"tftp_client.h"
#include<iostream>

using namespace std;
//用户打印信息的宏
#define ERR_LOG(msg) do { \
    perror(msg); \
    cout << __LINE__ << " " << __func__ << " " << __FILE__ << endl; \
} while(0)

//
TFTPClient::TFTPClient(const string & serverIP)
{
    //创建udp 套接字
    this->sfd =socket(AF_INET,SOCK_DGRAM,0);
    if(sfd<0)
    {
        ERR_LOG("sock error");
        return ;
    }

    //设置地址信息结构体
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    server_addr.sin_addr.s_addr = inet_addr(serverIP.c_str());  //服务器的ip地址

}
TFTPClient::~TFTPClient(){
    if(sfd>0)
    {
        close(sfd);
    }
}

void TFTPClient::run()
{
    while(true)
    {
        //展示菜单
        showMenu();
        char choice;    //菜单选项
        cin>>choice;    //输入选项

        waitForInput(); //吸收垃圾字符

        switch(choice)
        {
            case '1':
            doDownload();
            break;
            case '2' :
            doUPload();
            break;
            case '3':
            return ;

            default:
                cout<<"您输入的功能有误,请重新输入"<<endl;
                break;

        }

        clearScream();
    }
}

//下载函数
int TFTPClient::doDownload()
{
    string filename;//存储要下载的文件名
    cout<<"请输入要下载的文件名称";
   // cin>>filename;//这种只能输入没有空格的字符串 
    getline(cin,filename);//这种输入既可以输入无空格,也可以有空格的字符串

    //封装下载请求
    char buf[BUFFER_SIZE] = "";
    //封装请求包
    int size = sprintf(buf,"%c%c%s%c%s%c",0,1,filename.c_str(),0,"octer",0);//file
    if(sendto(sfd,buf,size,0,(struct sockaddr*)&server_addr,sizeof(server_addr))==-1)
    {
        ERR_LOG("sendto error");
        return -1;
    };

    //循环接收服务器发来的数据包
    size_t recv_len;
    unsigned short num =1; //记录数据快编号
    socklen_t addrlen = sizeof(server_addr);

    //定义有关文件操作的遍历
    int flag =0 ;//表示文件是否被打开
    int fd; //文件描述符

    //循环启动
    while(1)
    {
        //清空消息容器
        bzero(buf,BUFFER_SIZE);

        //读取服务器发来的消息
        recv_len = recvfrom(sfd,buf,BUFFER_SIZE,0,(struct sockaddr*)&server_addr,&addrlen); 
        if(recv_len <0)
        {
            ERR_LOG("recvfrom error");
            return -1;
        }


        //对读取下来的数据进行分析
        //判断是否为数据包
        if(3 == buf[1])
        {
            //读取数据操作
            //打开文件操作
            if(0==flag)
            {
                fd = open(filename.c_str(),O_WRONLY|O_CREAT|O_TRUNC,0664);
                if (fd <0)
                {
                   ERR_LOG("open error");
                   return -1;
                }
                //更改标识位
                flag = 1;   
            }
            //判断数据
            if(htons(num)==*(unsigned short *)(buf+2)) //表示是传入的数据包 num是自增的
            {
                //将数据包中的数据传入
              if( write(fd,buf+4,recv_len-4)<0)
              {
                cout<<"fd= "<<fd<<"     recv_len = "<<recv_len<<endl;
                ERR_LOG("write error");
                close(fd);
                return -1;
              }
            }
            //组装ack包,发给服务器
            buf[1] = 4;//ACk
            if(sendto(sfd,buf,4,0,(struct sockaddr*)&server_addr,sizeof(server_addr))<0)
            {
                /* data */
                ERR_LOG("sendto error");\
                
                close(fd);
                return -1;
            }
            //判断收取的数据长度 是否小于容器长度
            //表示数据包是最后一个数据包
            if(recv_len < BUFFER_SIZE) //表示数据包下载完毕
             {
                cout<<" 数据包下载完毕-----------"<<endl;
                close(fd);
                break;
             }
                num++;
        }else if(5 ==buf[1])
        {

            
            cout<<"_____error"<<(buf+4)<<"_______"<<endl;
            if(flag == 1)
            {
                close(fd);
            }
            break;
        }

        return 0;
    }
    return 0;
}


//上传函数
int TFTPClient::doUPload()
{
    string filename;
    cout<<" 请输入要上传的文件名: ";
    getline(cin,filename);

    //检查文件是否存在
    int fd = open(filename.c_str(),O_RDONLY);
    if(fd<0)
    {
        if(errno == ENOENT)
        {
                cout<<"文件不存在"<<endl;
                return -2;
        }
        ERR_LOG("open error");
        return -1;
    }

    cout<<"程序执行至此,表示文件存在\n"<<endl;

    //构造上传请求
    char buf[BUFFER_SIZE] = "";
    int size = sprintf(buf,"%c%c%s%c%s%c",0,2,filename.c_str(),0,"octet",0);
    if(sendto(sfd,buf,size,0,(struct sockaddr*)&server_addr,sizeof(server_addr))<0)
    {
        ERR_LOG("sendto error");
        close(fd);
        return -1;
    }

    //循环接受服务器发来的消息
    int recv_len; //接收消息的长度
    unsigned short num = 0; //消息块
    socklen_t addrlen =sizeof(server_addr);

 while (true)
 {
     // 清空容器
     bzero(buf, BUFFER_SIZE);
     // 读取服务器发来的消息
     recv_len = recvfrom(sfd, buf, BUFFER_SIZE, 0, (struct sockaddr *)&server_addr, &addrlen);
     if (recv_len < 0)
     {
         ERR_LOG("recvfrom error");
         close(fd);
         return -1;
     }

     // 判断消息类型
     if (4 == buf[1])
     {
         // 表示收到的是服务器发来的ACK包
         // 判断块编号
         if (num == ntohs(*(unsigned short *)(buf + 2)))
         {
             buf[1] = 3;                                // 修改为数据包
             num++;                                     // 块编号自增
             *(unsigned short *)(buf + 2) = htons(num); // 修改块编号
         }

         // 组装下一个数据包
         // 从文件中读取数据
         int res = read(fd, buf + 4, BUFFER_SIZE - 4);
         if (res < 0) // 说明读取失败
         {
             ERR_LOG("read error");
             close(fd);
             break;
         }
         else if (res == 0) // 说明文件读完了
         {
             cout << "------------上传完毕-------------" << endl;
             break;
         }

         // 向服务器发送数据包
         if (sendto(sfd, buf, res + 4, 0, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
         {
             ERR_LOG("sendto error");
             close(fd);
             return -1;
         }
     }
     else if (5 == buf[1])
     {
         // 表示服务器发来的是错误包
         cout<<"判断点\n"<<endl;
        for(int i =0;i<sizeof(buf);i++)
        {
            printf("%c", (unsigned char)buf[i]);
        }
        cout<<"           \n"<<endl;
         cout << "------------文件上传失败，请检查网络-------------" << endl;
         break;
     }
    }
    return 0;
}

//清屏函数
void TFTPClient::clearScream()
{
    cout<<"请输入任意字符进行清屏";
    while(getchar()!='\n');
}

//吸收垃圾字符的函数
void TFTPClient::waitForInput()
{
    while(getchar()!='\n');
}

//菜单函数的定义
void TFTPClient::showMenu()
{
    //清屏
    system("clear");
    cout<<"************基于udp的文件传输**************"<<endl;
    cout<<"1 下载"<<endl;
    cout<<"2 上传"<<endl;
    cout<<"3 退出"<<endl;


}
